package tr.edu.metu.is.mega.client.user;

import java.util.ArrayList;
import java.util.List;

import tr.edu.metu.is.mega.client.core.MainWindow;
import tr.edu.metu.is.mega.client.core.MegaUtil;
import tr.edu.metu.is.mega.client.resource.MEGAResources;
import tr.edu.metu.is.mega.client.service.GwtUserService;
import tr.edu.metu.is.mega.client.service.GwtUserServiceAsync;
import tr.edu.metu.is.mega.client.widgets.MegaComboBox;
import tr.edu.metu.is.mega.client.widgets.MegaTextField;
import tr.edu.metu.is.mega.common.dto.UserDTO;
import tr.edu.metu.is.mega.common.dto.UserRole;

import com.extjs.gxt.ui.client.Style.HorizontalAlignment;
import com.extjs.gxt.ui.client.Style.Orientation;
import com.extjs.gxt.ui.client.Style.SelectionMode;
import com.extjs.gxt.ui.client.binding.FieldBinding;
import com.extjs.gxt.ui.client.binding.FormBinding;
import com.extjs.gxt.ui.client.data.BasePagingLoader;
import com.extjs.gxt.ui.client.data.BeanModel;
import com.extjs.gxt.ui.client.data.BeanModelFactory;
import com.extjs.gxt.ui.client.data.BeanModelLookup;
import com.extjs.gxt.ui.client.data.BeanModelReader;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.PagingLoadConfig;
import com.extjs.gxt.ui.client.data.PagingLoadResult;
import com.extjs.gxt.ui.client.data.RpcProxy;
import com.extjs.gxt.ui.client.event.ButtonEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.GridEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.store.StoreEvent;
import com.extjs.gxt.ui.client.store.StoreListener;
import com.extjs.gxt.ui.client.util.Margins;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.form.FormPanel;
import com.extjs.gxt.ui.client.widget.form.FormPanel.LabelAlign;
import com.extjs.gxt.ui.client.widget.grid.CheckBoxSelectionModel;
import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
import com.extjs.gxt.ui.client.widget.grid.Grid;
import com.extjs.gxt.ui.client.widget.layout.ColumnLayout;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.extjs.gxt.ui.client.widget.layout.FormData;
import com.extjs.gxt.ui.client.widget.layout.FormLayout;
import com.extjs.gxt.ui.client.widget.layout.RowData;
import com.extjs.gxt.ui.client.widget.layout.RowLayout;
import com.extjs.gxt.ui.client.widget.toolbar.PagingToolBar;
import com.extjs.gxt.ui.client.widget.toolbar.PagingToolBar.PagingToolBarMessages;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * User search component.
 * 
 */
public class UserSearchWindow extends MainWindow {

    private static final int PAGE_SIZE = 20;

    private static final int GRIDVIEW_HEIGHT = 250;

    private FormBinding searchFormBinding;

    private Grid<BeanModel> gridUser;

    private List<UserDTO> selectedItems;

    private final boolean selectEnabled;

    private final boolean multiSelectEnabled;

    private final UserRole userRole;

    /**
     * Default constructor.
     */
    public UserSearchWindow() {
        this(false, true, null);
    }

    /**
     * Default constructor.
     * 
     * @param multiSelectEnabled set true to enable multi selection.
     */
    public UserSearchWindow(boolean multiSelectEnabled) {
        this(multiSelectEnabled, true, null);
    }

    /**
     * Default constructor.
     * 
     * @param multiSelectEnabled set true to enable multi selection.
     * @param selectEnabled set true to enable record selection.
     */
    private UserSearchWindow(boolean multiSelectEnabled, boolean selectEnabled, UserRole userRole) {
        super("UserSearchWindow", null);
        setHeading(MEGAResources.RESOURCEBUNDLE.usersearchwindow_userSearchTitle());

        this.multiSelectEnabled = multiSelectEnabled;
        this.selectEnabled = selectEnabled;
        this.userRole = userRole;

        setHeaderVisible(false);
        setBodyBorder(false);
    }

    /**
     * Default constructor.
     * 
     * @param multiSelect
     * @param userRole
     */
    public UserSearchWindow(boolean multiSelect, UserRole userRole) {
        this(multiSelect, true, userRole);
    }

    /**
     * returns GwtUserService instance.
     * 
     * @return GwtUserServiceAsync
     */
    private static GwtUserServiceAsync getUserService() {
        return GwtUserService.Util.getInstance();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onRender(Element parent, int index) {
        super.onRender(parent, index);

        final BasePagingLoader<PagingLoadResult<ModelData>> userLoader = new BasePagingLoader<PagingLoadResult<ModelData>>(getRpcProxy(),
                new BeanModelReader());
        userLoader.setRemoteSort(true);

        PagingToolBar pagingToolBar = new PagingToolBar(PAGE_SIZE);
        PagingToolBarMessages messages = new PagingToolBarMessages();
        messages.setEmptyMsg(MEGAResources.RESOURCEBUNDLE.gridEmptyText());
        pagingToolBar.setMessages(messages);
        pagingToolBar.bind(userLoader);

        ListStore<BeanModel> userStore = new ListStore<BeanModel>(userLoader);

        CheckBoxSelectionModel<BeanModel> cbsm = new CheckBoxSelectionModel<BeanModel>();

        if (multiSelectEnabled) {
            cbsm.setSelectionMode(SelectionMode.MULTI);
        } else {
            cbsm.setSelectionMode(SelectionMode.SINGLE);
        }

        gridUser = new Grid<BeanModel>(userStore, getColumnModel(cbsm));

        userStore.addStoreListener(new StoreListener<BeanModel>() {

            /**
             * {@inheritDoc}
             */
            @Override
            public void storeDataChanged(StoreEvent<BeanModel> se) {
                super.storeDataChanged(se);

                if (selectedItems != null && selectedItems.size() > 0 && selectEnabled) {
                    BeanModelFactory factory = BeanModelLookup.get().getFactory(UserDTO.class);
                    gridUser.getSelectionModel().setSelection(factory.createModel(selectedItems));
                }
            }
        });

        gridUser.addListener(Events.Attach, new Listener<GridEvent<BeanModel>>() {

            @Override
            public void handleEvent(GridEvent<BeanModel> be) {
                userLoader.load(0, PAGE_SIZE);
            }
        });

        MegaUtil.decoradeGridViewWithAutoHeight(gridUser, false);
        gridUser.setHeight(GRIDVIEW_HEIGHT);

        gridUser.setSelectionModel(cbsm);
        gridUser.addPlugin(cbsm);

        FormPanel searchForm = initSearchForm();

        ContentPanel gridPanel = new ContentPanel();
        gridPanel.setHeaderVisible(false);
        gridPanel.setHeight(220);
        gridPanel.setLayout(new FitLayout());

        gridPanel.add(gridUser);
        gridPanel.setBottomComponent(pagingToolBar);

        ContentPanel mainPanel = new ContentPanel();
        mainPanel.setHeaderVisible(false);
        mainPanel.setLayout(new RowLayout(Orientation.VERTICAL));
        mainPanel.setFrame(true);

        mainPanel.add(searchForm, new RowData(1, -1, new Margins(4)));
        mainPanel.add(gridPanel, new RowData(1, -1, new Margins(4)));

        add(mainPanel);
    }

    /**
     * Loads the data using the current configuration.
     * 
     * @return true if the load was requested
     */
    private boolean reloadGrid() {
        return gridUser.getStore().getLoader().load();
    }

    /**
     * Initialize search form.
     * 
     * @return FormPanel
     */
    private FormPanel initSearchForm() {
        FormPanel formPanel = new FormPanel();

        formPanel.setFrame(true);
        formPanel.setHeaderVisible(false);
        formPanel.setLabelAlign(LabelAlign.LEFT);

        LayoutContainer main = new LayoutContainer();
        main.setLayout(new ColumnLayout());

        LayoutContainer left = new LayoutContainer();
        left.setStyleAttribute("paddingRight", "10px");
        FormLayout layout = new FormLayout();
        layout.setLabelAlign(LabelAlign.LEFT);
        left.setLayout(layout);

        MegaTextField textFieldUserName = new MegaTextField(UserDTO.FIELD_USERNAME);
        textFieldUserName.setFieldLabel("User Name");
        left.add(textFieldUserName, new FormData("96%"));

        MegaTextField textFieldName = new MegaTextField(UserDTO.FIELD_NAME);
        textFieldName.setFieldLabel("Name");
        left.add(textFieldName, new FormData("96%"));

        MegaTextField textFieldSurname = new MegaTextField(UserDTO.FIELD_SURNAME);
        textFieldSurname.setFieldLabel("Surname");
        left.add(textFieldSurname, new FormData("96%"));

        LayoutContainer right = new LayoutContainer();
        right.setStyleAttribute("paddingLeft", "10px");
        layout = new FormLayout();
        layout.setLabelAlign(LabelAlign.LEFT);
        right.setLayout(layout);

        MegaComboBox<BeanModel> roleComboBox = UserEditDialog.createRoleComboBox(UserDTO.FIELD_ROLE);
        if (userRole != null) {
            BeanModelFactory beanModelFactory = BeanModelLookup.get().getFactory(UserRole.class);
            roleComboBox.select(beanModelFactory.createModel(userRole));
            roleComboBox.setEnabled(false);
        }
        right.add(roleComboBox, new FormData("96%"));

        MegaTextField textFieldIdentification = new MegaTextField(UserDTO.FIELD_IDENTIFICATIONNO);
        textFieldIdentification.setFieldLabel("Identification");
        right.add(textFieldIdentification, new FormData("96%"));

        MegaTextField textFieldEmail = new MegaTextField(UserDTO.FIELD_EMAIL);
        textFieldEmail.setFieldLabel("E-Mail");
        right.add(textFieldEmail, new FormData("96%"));

        main.add(left);
        main.add(right);

        formPanel.add(main);

        Button btnSearch = new Button(MEGAResources.RESOURCEBUNDLE.search(), new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent ce) {
                reloadGrid();
            }
        });

        Button btnCleanForm = new Button(MEGAResources.RESOURCEBUNDLE.clear(), new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent ce) {
                clearForm();
            }
        });

        formPanel.addButton(btnSearch);
        formPanel.addButton(btnCleanForm);
        formPanel.setButtonAlign(HorizontalAlignment.CENTER);

        searchFormBinding = new FormBinding(formPanel, true);
        searchFormBinding.bind(getNewModelData());

        return formPanel;
    }

    /**
     * Clears search form.
     */
    private void clearForm() {
        searchFormBinding.clear();
        reloadGrid();
    }

    /**
     * Creates a new GenPersonDTO model instance.
     * 
     * @return BeanModel
     */
    private BeanModel getNewModelData() {
        UserDTO genPersonDTO = new UserDTO();
        BeanModelFactory factory = BeanModelLookup.get().getFactory(UserDTO.class);
        BeanModel model = factory.createModel(genPersonDTO);
        return model;
    }

    /**
     * Returns Person gridview column model.
     * 
     * @param cbsm CheckBoxSelectionModel
     * @return ColumnModel
     */
    private ColumnModel getColumnModel(CheckBoxSelectionModel<BeanModel> cbsm) {
        List<ColumnConfig> configs = new ArrayList<ColumnConfig>();

        if (selectEnabled) {
            configs.add(cbsm.getColumn());
        }

        configs.add(new ColumnConfig(UserDTO.FIELD_NAME, MEGAResources.RESOURCEBUNDLE.name(), 50));
        configs.add(new ColumnConfig(UserDTO.FIELD_SURNAME, MEGAResources.RESOURCEBUNDLE.surname(), 50));
        configs.add(new ColumnConfig(UserDTO.FIELD_EMAIL, MEGAResources.RESOURCEBUNDLE.email(), 50));
        configs.add(new ColumnConfig(UserDTO.FIELD_ROLE, MEGAResources.RESOURCEBUNDLE.usermanagementwindow_role(), 50));
        configs.add(new ColumnConfig(UserDTO.FIELD_IDENTIFICATIONNO, MEGAResources.RESOURCEBUNDLE.usermanagementwindow_id(), 50));

        ColumnConfig columnConfig = new ColumnConfig(UserDTO.FIELD_EXPIRYDATE, MEGAResources.RESOURCEBUNDLE.expiredate(), 50);
        columnConfig.setRenderer(MegaUtil.getDateRenderer());
        configs.add(columnConfig);

        ColumnModel columnModel = new ColumnModel(configs);
        return columnModel;
    }

    /**
     * <code>DataProxy</code> implementation that retrieves user records using
     * GWT RPC.
     * 
     * @return RpcProxy
     */
    private RpcProxy<PagingLoadResult<UserDTO>> getRpcProxy() {
        RpcProxy<PagingLoadResult<UserDTO>> proxy = new RpcProxy<PagingLoadResult<UserDTO>>() {

            @Override
            protected void load(Object loadConfig, AsyncCallback<PagingLoadResult<UserDTO>> callback) {
                boolean isFieldsValid = isSearchValid();
                UserDTO userDTO = new UserDTO();
                userDTO.setRole(userRole);
                if (isFieldsValid) {
                    BeanModel userModel = (BeanModel) searchFormBinding.getModel();
                    userDTO = userModel.getBean();

                }
                getUserService().getUsers((PagingLoadConfig) loadConfig, userDTO, callback);
            }
        };
        return proxy;
    }

    /**
     * Validate search fields.
     * 
     * @return true if search form is valid.
     */
    protected boolean isSearchValid() {
        for (FieldBinding binding : searchFormBinding.getBindings()) {
            if (binding.getField().getValue() != null) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns selected records when multi selection enabled.
     * 
     * @return List<UserDTO> selected records
     */
    public List<UserDTO> getSelectedItems() {
        return MegaUtil.getSelectedItems(gridUser.getSelectionModel());
    }

    /**
     * Returns selected UserDTO instance.
     * 
     * @return UserDTO
     */
    public UserDTO getSelectedItem() {
        return MegaUtil.getSelectedItem(gridUser.getSelectionModel());
    }

    /**
     * Chooses selected record row from grid.
     * 
     * @param selectedUsers List<UserDTO>
     */
    public void setSelectedItems(List<UserDTO> selectedUsers) {
        this.selectedItems = selectedUsers;
    }

}
